package com.ekeyman.securecreditlib.business;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.ekeyman.securecredit.dto.CardTransactionGrid;
import com.ekeyman.securecreditlib.dao.CardTransactionDao;
import com.ekeyman.securecreditlib.domain.CardTransaction;
import com.ekeymanlib.dto.SearchFilter;

@Service("cardTransactionBO")
public class CardTransactionBO {

	@Autowired
	private CardTransactionDao cardTransactionDao;
	
	/*
	 * This wrapper is needed because @Transactional does not respect AOP ordering.
	 * This ensures that com.ekeyman.securecreditlib.aop.EncryptionAspect.encryptBeforeSaving()
	 * always executes BEFORE the transaction is started. Alternatively, the @Transactional
	 * annotation could be moved to the appropriate DAO objects. However, this may not be
	 * feasible as many times business object methods may invoke multiple DAO methods.
	 */
	public void saveCardTransaction(String creditCardId, CardTransaction cardTransaction){
		doSaveCardTransaction(creditCardId, cardTransaction);
	}
	
	@Transactional(propagation = Propagation.REQUIRES_NEW, isolation=Isolation.SERIALIZABLE)	
	private void doSaveCardTransaction(String creditCardId, CardTransaction cardTransaction){
		getCardTransactionDao().saveCardTransaction(Long.parseLong(creditCardId),cardTransaction);
	}	

	/*
	 * This wrapper is needed because @Transactional does not respect AOP ordering.
	 * This ensures that com.ekeyman.securecreditlib.aop.EncryptionAspect.decryptAfterSaving()
	 * always executes AFTER the transaction is completed. Alternatively, the @Transactional
	 * annotation could be moved to the appropriate DAO objects. However, this may not be
	 * feasible as many times business object methods may invoke multiple DAO methods.
	 */
	public CardTransaction findCardTransaction(String vendorCode){
		return doFindCardTransaction(vendorCode);
	}
	
	@Transactional(propagation = Propagation.REQUIRES_NEW, isolation=Isolation.READ_COMMITTED, readOnly=true)
	private CardTransaction doFindCardTransaction(String vendorCode){
		return getCardTransactionDao().findCardTransaction(vendorCode);
	}
	
	/*
	 * This wrapper is needed because @Transactional does not respect AOP ordering.
	 * This ensures that com.ekeyman.securecreditlib.aop.EncryptionAspect.decryptAfterSaving()
	 * always executes AFTER the transaction is completed. Alternatively, the @Transactional
	 * annotation could be moved to the appropriate DAO objects. However, this may not be
	 * feasible as many times business object methods may invoke multiple DAO methods.
	 */	
	public List<CardTransaction> listCardTransactions(List<SearchFilter> searchFilters, 
			String sortIndex, String sortOrder, int firstResult, int maxResults){
		return doListCardTransactions(searchFilters, sortIndex, sortOrder, firstResult, maxResults);
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW, isolation=Isolation.READ_COMMITTED, readOnly=true)
	public List<CardTransaction> doListCardTransactions(List<SearchFilter> searchFilters, 
			String sortIndex, String sortOrder, int firstResult, int maxResults){
		return getCardTransactionDao().listCardTransactions(searchFilters, sortIndex, sortOrder, firstResult, maxResults);
	}
	
	public List<CardTransactionGrid> gridList(List<SearchFilter> searchFilters, 
			String sortIndex, String sortOrder, int firstResult, int maxResults){
		List<CardTransaction> cardTransactions = listCardTransactions(searchFilters, sortIndex, sortOrder, firstResult, maxResults);	
		return gridTransformation(cardTransactions);
	}	

	
	
	public List<CardTransaction> listCreditCardTransactions(String creditCardId, List<SearchFilter> searchFilters, 
			String sortIndex, String sortOrder, int firstResult, int maxResults){
		return doListCreditCardTransactions(creditCardId, searchFilters, sortIndex, sortOrder, firstResult, maxResults);
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW, isolation=Isolation.READ_COMMITTED, readOnly=true)
	public List<CardTransaction> doListCreditCardTransactions(String creditCardId, List<SearchFilter> searchFilters, 
			String sortIndex, String sortOrder, int firstResult, int maxResults){
		return getCardTransactionDao().listCreditCardTransactions(creditCardId, searchFilters, sortIndex, sortOrder, firstResult, maxResults);
	}
	
	public List<CardTransactionGrid> gridCreditCardTransactionList(String creditCardId, List<SearchFilter> searchFilters, 
			String sortIndex, String sortOrder, int firstResult, int maxResults){
		List<CardTransaction> cardTransactions = listCreditCardTransactions(creditCardId, searchFilters, sortIndex, sortOrder, firstResult, maxResults);	
		return gridTransformation(creditCardId, cardTransactions);
	}		
	
	// Remember the encryption AOP pointcut is for save* not update*
	public void saveExistingCreditCardTransaction(CardTransactionGrid cardTransactionGrid){
		doSaveExistingCreditCardTransaction(cardTransactionGrid);
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW, isolation=Isolation.SERIALIZABLE)
	private void doSaveExistingCreditCardTransaction(CardTransactionGrid cardTransactionGrid){
		getCardTransactionDao().updateCardTransaction(cardTransactionGrid);
	}
	
	
	@Transactional(propagation = Propagation.REQUIRES_NEW, isolation=Isolation.SERIALIZABLE)
	public void deleteCardTransaction(long id){
		getCardTransactionDao().deleteCardTransaction(id);
	}
	
	@Transactional(propagation = Propagation.REQUIRES_NEW, isolation=Isolation.READ_COMMITTED, readOnly=true)
	public long getCardTransactionCount(List<SearchFilter> searchFilters){
		return getCardTransactionDao().getCardTransactionCount(searchFilters);
	}
	
	@Transactional(propagation = Propagation.REQUIRES_NEW, isolation=Isolation.READ_COMMITTED, readOnly=true)
	public long getCreditCardTransactionCount(String creditCardId, List<SearchFilter> searchFilters){
		return getCardTransactionDao().getCreditCardTransactionCount(creditCardId, searchFilters);
	}
	
	private List<CardTransactionGrid> gridTransformation(List<CardTransaction> cardTransactions){
		return gridTransformation(null, cardTransactions);
	}
	
	private List<CardTransactionGrid> gridTransformation(String creditCardId, List<CardTransaction> cardTransactions){
		
		List<CardTransactionGrid> gridCardTransactions = new ArrayList<CardTransactionGrid>();
		
		for(CardTransaction ct : cardTransactions){
			CardTransactionGrid ctg = new CardTransactionGrid();
			ctg.setId(ct.getId());
			ctg.setTransactionNumber(ct.getTransactionNumber());
			ctg.setVendorCode(ct.getVendorCode());
			ctg.setTransactionDate(ct.getTransactionDate());
			ctg.setPostDate(ct.getPostDate());			
			ctg.setAmount(ct.getAmount().toString());
			ctg.setCreditCardId(creditCardId);
			gridCardTransactions.add(ctg);
		}
		
		return gridCardTransactions;
	}
	
	
	
	public void setCardTransactionDao(CardTransactionDao cardTransactionDao) {
		this.cardTransactionDao = cardTransactionDao;
	}

	public CardTransactionDao getCardTransactionDao() {
		return cardTransactionDao;
	}
}
